using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using EnvDTE80;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using EnvDTE;
using Microsoft.VisualStudio.TeamFoundation;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.ComponentModel;

namespace TeamReview.Services
{
	public static class TFSService
	{
		private static TeamFoundationServer tfs;
		private static readonly SortedList<String, List<string>> users = new SortedList<String, List<string>>();
		private static readonly object userLock = new Object();
		private static TeamProject teamProject;
		private static WorkItemStore workItemStore;
		private static FieldDefinitionCollection fields;
		private static AllowedValuesCollection priorityAllowedValues;
		private static DTE2 shellDTE;
		private static SolutionEvents solutionEvents;
		internal static event TeamProjectChangeEventHandler TeamProjectChanged;
		private static VersionControlServer versionControlServer;
		private static TeamFoundationServerExt tfsExt;
		private static VersionControlExt versionControl;

		private static void OnTeamProjectChanged()
		{

			if (teamProject == null)
				Instrumentation.Trace("TeamReview is not available because the current solution is not part of a Team Project.");
			else
				Instrumentation.Trace(String.Format(@"Team Project changed to {0}\{1}",
										  teamProject.TeamFoundationServer.Name, teamProject.Name));

			if (TeamProjectChanged == null)
				return;

			TeamProjectChanged();
		}


		internal static VersionControlServer VersionControlServer
		{
			get { return versionControlServer; }
		}

		internal static TeamProject TeamProject
		{
			get { return teamProject; }
		}

		internal static VersionControlExt VersionControlExt
		{
			get { return versionControl; }
		}

		internal static TeamFoundationServerExt TeamFoundationServerExt
		{
			get { return tfsExt; }
		}

		internal static TeamFoundationServer TeamFoundationServer
		{
			get { return tfs; }
		}

		internal static WorkItemStore WorkItemStore
		{
			get { return workItemStore; }
		}



		internal static string CurrentUserDisplayName
		{
			get
			{
				if (workItemStore.UserDisplayMode == UserDisplayMode.AccountName)
					return workItemStore.TeamFoundationServer.AuthenticatedUserName;

				return workItemStore.TeamFoundationServer.AuthenticatedUserDisplayName;
			}
		}

		internal static bool IsTFSProject
		{
			get
			{
				return !(teamProject == null);
			}
		}

		public static void Configure(DTE2 dte)
		{
			shellDTE = dte;
			solutionEvents = shellDTE.Events.SolutionEvents;
			solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
			SetTeamProject();
		}

		public static void Shutdown()
		{
			try
			{
				solutionEvents.Opened -= new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
			}
			catch { }
		}


		internal static bool HasField(string fieldName)
		{
			return fields.Contains(fieldName);
		}


		static void SolutionEvents_Opened()
		{
			SetTeamProject();
		}


		internal static BindingList<BindingItemQuery> GetAvailableTeamQueries()
		{
			StoredQueryCollection sq = workItemStore.Projects[teamProject.Name].StoredQueries;			
			BindingList<BindingItemQuery> bl = new BindingList<BindingItemQuery>();
			
			foreach (StoredQuery q in sq)
				bl.Add(new BindingItemQuery(String.Format("{0} - {1}", q.QueryScope.ToString("G"), q.Name), q.QueryGuid));
			
			return bl;
		}

		internal static AllowedValuesCollection PriorityAllowedValues
		{
			get { return priorityAllowedValues; }
		}

		internal static void SetTeamProject()
		{

			TeamProject newProject;

			if (shellDTE.Solution == null)
				newProject = null;
			else
			{
				string solution = "not open";
				if (!String.IsNullOrEmpty(shellDTE.Solution.FullName))
					solution = shellDTE.Solution.FullName;

				Instrumentation.Trace(String.Format(@"Visual Studio Solution is {0}", solution));


				versionControl = shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt") as VersionControlExt;
				if (versionControl == null || versionControl.SolutionWorkspace == null)
					newProject = null;
				else
					newProject = versionControl.SolutionWorkspace.GetTeamProjectForLocalPath(shellDTE.Solution.FullName);
			}

			if (teamProject != newProject)
			{
				teamProject = newProject;

				if (teamProject == null)
				{
					OnTeamProjectChanged();
					return;
				}


				tfs = teamProject.TeamFoundationServer;
				tfsExt = (TeamFoundationServerExt)shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt");
				versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

				workItemStore = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
				if (workItemStore == null)
					throw new NullReferenceException("Did not retrieve WorkItemStore from Team Foundation Server");

				if (!workItemStore.Projects[teamProject.Name].WorkItemTypes.Contains(Constants.CODE_REVIEW_ITEM_TYPE))
				{
					teamProject = null;
					OnTeamProjectChanged();
					return;
				}

				fields = workItemStore.Projects[teamProject.Name].WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE].FieldDefinitions;

				if (fields.Contains(Constants.CODE_PRIORITY_FIELD))
				{
					priorityAllowedValues = fields[Constants.CODE_PRIORITY_FIELD].AllowedValues;

				}
				else
				{
					priorityAllowedValues = null;
				}

				System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(SetUsers));
				thread.Start();
				OnTeamProjectChanged();
			}
		}

		internal static BindingList<BindingItemCodeReview> GetAvailableQueryWorkItems(Guid queryGuid)
		{
			StoredQuery scQuery = TFSService.WorkItemStore.GetStoredQuery(queryGuid);
			string project = String.Format("'{0}'", TFSService.TeamProject.Name);
			string me = String.Format("'{0}'", TFSService.TeamProject.TeamFoundationServer.AuthenticatedUserDisplayName);
			string text = scQuery.QueryText.Replace("@project", project).Replace("@me", me);
			Query query = new Query(TFSService.WorkItemStore, text);
			query.SortFieldList.Clear();
			query.SortFieldList.Add("System.Id", SortType.Ascending);

			WorkItemCollection items = TFSService.WorkItemStore.Query(query.QueryString);

				BindingList<BindingItemCodeReview> bl = new BindingList<BindingItemCodeReview>();
				for (int i = 0; i < items.Count; i++)
					bl.Add(new BindingItemCodeReview(items[i].Title, items[i].Id));

				return bl;
		}

		internal static TeamProject GetTeamProject()
		{
			return teamProject;
		}

		private static void SetUsers()
		{
			SetUsers(teamProject.ArtifactUri);
			
		}

		private static void SetUsers(Uri projectUri)
		{
			if (tfs == null)
				return;

			string uriString = projectUri.ToString();
			if (users.ContainsKey(uriString))
			{
				lock (userLock)
				{
					if (users.ContainsKey(uriString))
						return;
				}
			}

			lock (userLock)
			{
				users.Add(uriString, new List<string>());
				users[uriString].AddRange(GetCodeChangers(projectUri));
			}
		}

		internal static List<String> GetCodeChangers(Uri projectUri)
		{
			List<String> codeChangers = new List<String>();
			IGroupSecurityService gss = (IGroupSecurityService)tfs.GetService(typeof(IGroupSecurityService));

			Identity[] projectGroups = gss.ListApplicationGroups(projectUri.ToString());
			Identity serviceGroup = gss.ReadIdentity(SearchFactor.ServiceApplicationGroup, null, QueryMembership.None);
			Identity[] serviceGroups = new Identity[1] { serviceGroup };
			Identity[] groups = new Identity[projectGroups.Length + 1];
			serviceGroups.CopyTo(groups, 0);
			projectGroups.CopyTo(groups, 1);

			SortedDictionary<MemberReader, System.Threading.Thread> threads = new SortedDictionary<MemberReader, System.Threading.Thread>();
			foreach (Identity group in projectGroups)
			{
				if (!(group.DisplayName.Equals("Contributors") ||
					  group.DisplayName.Equals("Project Administrators") ||
					  group.DisplayName.Equals("Service Accounts")))
					continue;
				MemberReader memberReader = new MemberReader(group, gss);
				
				threads.Add(memberReader, new System.Threading.Thread(new ThreadStart(memberReader.Read)));
				threads[memberReader].Start();
			}

			foreach(MemberReader reader in threads.Keys)
			{
				threads[reader].Join();
				foreach (string member in reader.MemberNames)
				{
					if (!codeChangers.Contains(member))
						codeChangers.Add(member);
				}
			}

			codeChangers.Sort();
			return codeChangers;
		}

		internal static List<string> GetTeamUsers(Microsoft.TeamFoundation.WorkItemTracking.Client.Project project)
		{
			

			SetUsers(project.Uri);
			lock (userLock)
				return users[project.Uri.ToString()];
			

		}

		internal static WorkItemTypeCollection GetAvailableTeamWorkItemTypes
		{
			get { return workItemStore.Projects[teamProject.Name].WorkItemTypes; }
		}

		internal static Microsoft.TeamFoundation.WorkItemTracking.Client.Project GetQueryProject(Guid queryGuid)
		{
            StoredQuery query = workItemStore.Projects[teamProject.Name].StoredQueries[queryGuid];
            if (!query.QueryText.Contains("[System.TeamProject] = @project"))
            {
            
                string firstFind = "[System.TeamProject] = '";
                int firstPos = query.QueryText.IndexOf(firstFind);
                int secondPos = query.QueryText.IndexOf("'", firstPos + firstFind.Length + 1);
                string project = query.QueryText.Substring((firstPos + firstFind.Length), (secondPos - firstPos  - firstFind.Length));

                return workItemStore.Projects[project];
            }
            else
            {
                return query.Project;
            }

		}

		
	}
}
